#pragma once

#include <iostream>
#include <functional>
#include <cstring>
#include "LogMessage.hpp"
#include "EventHandler.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"

void SetNonBlocking(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl == -1)
    {
        lg.LogMessage(Error, "fcntl error, errno: %d, strerror: %s", errno, strerror(errno));
    }
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

// listen套接字的读事件
class Accepter
{
public:
    // 主线程用于专门读取新连接，并将新连接直接交给其他线程
    void ListenAccepter(Socket *listensock, TcpServer *Rector)
    {
        if (listensock == nullptr || Rector == nullptr)
        {
            lg.LogMessage(Warning, "ListenAccept error, TcpSocket: %p, TcpServer: %p", listensock, Rector);
            return;
        }

        while (true)
        {
            InetAddr addr;
            // 1. 非阻塞接受新连接
            Socket *newsock = listensock->AcceptConnect(addr);
            if (newsock != nullptr)
            {
                lg.LogMessage(Debug, "get a new link, sockfd: %d", newsock->GetSockfd());

                // 获取新连接后，直接交给其他线程
                int sockfd = newsock->GetSockfd();
                int fd = Rector->_pipelines[sockfd % Rector->_threadsize];

                // 让message保持固定大小4个字节
                std::string message = std::to_string(sockfd);
                while (message.size() < 4)
                    message.push_back(' ');

                lg.LogMessage(Debug, "send [%s] to %d", message.c_str(), fd);
                SendSock(fd, message);
            }
            else
            {
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg.LogMessage(Warning, "Get g new link error");
                }
            }
        }
    }

    void SendSock(int fd, std::string message)
    {
        if (message.size() != 4)
        {
            lg.LogMessage(Warning, "message size is not 4");
            return;
        }

        while (!message.empty())
        {
            ssize_t n = write(fd, message.c_str(), message.size());
            if (n >= 0)
            {
                message.erase(0, n);
                if (message.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN) // 发送缓冲区已经满了，不能再发了
                    break;
                else if (errno == EINTR) // 被信号中断了，重新发送
                    continue;
                else
                {
                    lg.LogMessage(Warning, "write error");
                    return;
                }
            }
        }
    }

    void AcceptSock(Epoller *epoll, TcpServer *Rector)
    {
        if (epoll == nullptr || Rector == nullptr)
        {
            lg.LogMessage(Warning, "TcpSocket: %p Epoll: %p, TcpServer: %p",epoll, Rector);
            return;
        }

        std::string threadname = Rector->_threadname;
        int threadid = Rector->fd;
        while (true)
        {
            //每次只读取固定的4个字节
            char buffer[5];
            int n = read(threadid, buffer, 4);
            if (n > 0)
            {
                buffer[n] = 0;
                int sockfd = atoi(buffer);

                lg.LogMessage(Debug, "%s get a new sock from , sockfd: %d",threadname.c_str(), sockfd);
                Socket* sock = new TcpSocket;
                sock->SetSockfd(sockfd);
                // 2. 设置新连接为非阻塞
                SetNonBlocking(sockfd);
                // 3. 添加到epoll模型当中
                epoll->AddEpoll(sock->GetSockfd(), EPOLLIN | EPOLLET);
                // 4. 创建Connect对象
                auto reader = std::bind(&EventHandler::Reader, std::placeholders::_1);
                auto writer = std::bind(&EventHandler::Writer, std::placeholders::_1);
                auto excepter = std::bind(&EventHandler::Excepter, std::placeholders::_1);
                Connect *conn = new Connect(sock, Rector);
                conn->Establishment(reader, writer, excepter);
                // 5. 添加Connect对象
                Rector->AddConnection(sock->GetSockfd(), conn);

                lg.LogMessage(Debug, "Add epoll success");
            }
            else
            {
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg.LogMessage(Warning, "Get g new link error");
                }
            }
        }
    }

private:
};